/////////////////////////////////////////////////////////////////////////////
// Name:        ax_view.cpp
// Purpose:     Accessibility view class
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/app.h"
#include "wx/filename.h"

#include "ax/ax_view.h"
#include "ax/ax_menu.h"
#include "ax/ax_pagerctrl.h"
#include "ax/ax_view_summary.h"
#include "ax/ax_speech.h"

/*
 * AxView class declaration
 * Handles UI for a family of resources
 */

IMPLEMENT_CLASS( AxView, wxEvtHandler )

void AxView::Init()
{
    m_window = NULL;
    m_document = NULL;
    m_live = false;
    m_active = false;
    m_viewManager = NULL;
    m_channel = AxSpeechMainChannel;
}

bool AxView::AssociateDocument(AxDocument* doc)
{
    SetDocument(doc);
    return true;
}

// Override if something needs to be done before the menu context changes, e.g.
// saving an edit. If this returns false, it vetoes the menu change.
bool AxView::OnChangeMenuPre(AxMenu* WXUNUSED(currentMenu), AxMenu* WXUNUSED(newMenu))
{
    // TODO: save if necessary
    return true;
}

// Shows the current window in the pager control
bool AxView::ShowWindow(AxViewManager* viewManager)
{
    int page = viewManager->GetViewPagerCtrl()->FindPage(GetWindow());
    if (page != -1)
    {
        wxWindow* lastFocus = wxWindow::FindFocus();
        viewManager->GetViewPagerCtrl()->SetSelection(page);
        if (lastFocus)
            lastFocus->SetFocus();
        return true;
    }
    else
        return false;
}

// Gets the speech object.
AxSpeech* AxView::GetSpeech() const
{
    AxSpeech* speech = NULL;
    if (GetViewManager())
        speech = & GetViewManager()->GetSpeech();
    return speech;
}

// Returns the speech channel to be used by this adapter.
AxSpeechChannel AxView::GetChannel(AxSpeechChannel channel) const
{
    if (channel == AxSpeechDefaultChannel)
        channel = m_channel;

    if (channel != AxSpeechMainChannel && channel != AxSpeechContentChannel)
        channel = AxSpeechMainChannel;
    return channel;
}

// Say some text
bool AxView::Say(const wxString& text, int flags, int verbosity)
{
    AxSpeech* speech = GetSpeech();
    if (speech)
    {
        if (GetChannel() == AxSpeechContentChannel)
            flags |= AxSpeechFlagsContentChannel;

        AxSpeechEvent speechEvent;
        speechEvent.SetSpeechEventType(AxSpeechEventText);
        speechEvent.SetVerbosityLevel(verbosity);
        speechEvent.SetText(text);
        speechEvent.SetFlags(flags);
        speech->EmitSpeech(speechEvent);
        return true;
    }
    else
        return false;
}

// Say some text
bool AxView::Say(AxSpeechChannel channel, const wxString& text, int flags, int verbosity)
{
    if (GetChannel(channel) == AxSpeechContentChannel)
        flags |= AxSpeechFlagsContentChannel;
    return Say(text, flags, verbosity);
}

bool AxView::StopSpeech(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (channel == AxSpeechAllChannels)
        {
            GetSpeech()->Stop(AxSpeechMainChannel);
            GetSpeech()->Stop(AxSpeechContentChannel);
        }
        else
            GetSpeech()->Stop(GetChannel(channel));

        return true;
    }
    else
        return false;
}

// Pause or resume speech
bool AxView::PauseOrResumeSpeech(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (channel == AxSpeechAllChannels)
        {
            GetSpeech()->PauseOrResume(AxSpeechMainChannel);
            GetSpeech()->PauseOrResume(AxSpeechContentChannel);
        }
        else
            GetSpeech()->PauseOrResume(GetChannel(channel));
        return true;
    }
    else
        return false;
}

// Are we speaking?
bool AxView::IsSpeaking(AxSpeechChannel channel) const
{
    return GetSpeech() && GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsSpeaking();
}

// Can we stop?
bool AxView::CanStop(AxSpeechChannel channel) const
{
    return GetSpeech() && (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsSpeaking() || GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPaused());
}

// Can we pause?
bool AxView::CanPause(AxSpeechChannel channel) const
{
    return GetSpeech() && (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsSpeaking() && !GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPaused());
}

// Can we resume?
bool AxView::CanResume(AxSpeechChannel channel) const
{
    return GetSpeech() && GetSpeech()->GetTextToSpeech(GetChannel(GetChannel(channel))).IsPaused();
}

// Skip forward
bool AxView::SkipForward(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPlaying() && GetSpeech()->GetTextToSpeech(GetChannel(channel)).HasCapability(wxTTS_CAP_SKIP))
            GetSpeech()->GetTextToSpeech(GetChannel(channel)).Skip(1);
        return true;
    }
    else
        return false;
}

// Skip forward
bool AxView::SkipBack(AxSpeechChannel channel)
{
    if (GetSpeech())
    {
        if (GetSpeech()->GetTextToSpeech(GetChannel(channel)).IsPlaying() && GetSpeech()->GetTextToSpeech(GetChannel(channel)).HasCapability(wxTTS_CAP_SKIP))
            GetSpeech()->GetTextToSpeech(GetChannel(channel)).Skip(-1);
        return true;
    }
    else
        return false;
}

/*
 * AxViewManager class declaration
 * Manages views, resources, etc.
 */

IMPLEMENT_CLASS( AxViewManager, wxEvtHandler )

AxViewManager* AxViewManager::sm_viewManager = NULL;

AxViewManager::~AxViewManager()
{
    sm_viewManager = NULL;

    Clear();
}

void AxViewManager::Init()
{
    sm_viewManager = this;
    m_activeView = NULL;
    m_menuCtrl = NULL;
    m_containerCtrl = NULL;
    m_titleCtrl = NULL;
    m_descriptionCtrl = NULL;
    m_viewPagerCtrl = NULL;
    m_menuController = NULL;
    m_shortcutManager = NULL;
    m_modalCount = 0;
}

// Set the controller object.
void AxViewManager::SetMenuController(AxMenuController* controller)
{
    if (m_menuController)
        delete m_menuController;

    m_menuController = controller;
}

bool AxViewManager::ActivateView(AxView* view, AxDocument* doc)
{
    if (m_activeView && view == m_activeView)
        return true;
    if (m_activeView)
        DeactivateView(m_activeView);

    if (view->Activate(this, doc))
    {
        view->SetActive(true);
        m_activeView = view;
        return true;
    }
    else
        return false;
}

bool AxViewManager::DeactivateView(AxView* view)
{
    wxASSERT(view == m_activeView);

    if (view->Deactivate(this))
    {
        if (m_activeView == view)
            m_activeView = NULL;
        view->SetActive(false);
        return true;
    }
    else
        return false;
}

void AxViewManager::ClearViews()
{
    if (m_activeView)
        DeactivateView(m_activeView);

    while (m_views.GetCount() > 0)
    {
        AxView* view = m_views[0];
        view->Uninitialize(this);
        delete view;
        m_views.RemoveAt(0);
    }
}

// Clear everything.
void AxViewManager::Clear()
{
    ClearViews();

    SetMenuController(NULL);
}

// Find view
AxView* AxViewManager::FindView(wxClassInfo* info) const
{
    size_t i;
    for (i = 0; i < m_views.GetCount(); i++)
    {
        AxView* view = m_views[i];
        if (view->GetClassInfo() == info)
            return view;
    }
    return NULL;
}

// Find or create view, and add to view list if necessary
AxView* AxViewManager::FindOrCreateView(wxClassInfo* info)
{
    AxView* view = FindView(info);
    if (view)
        return view;
    else
    {
        view = wxDynamicCast(info->CreateObject(), AxView);
        if (view)
        {
            view->SetViewManager(this);
            m_views.Add(view);
        }
        return view;
    }
}


// Show a top-level menu
bool AxViewManager::ShowInitialMenu(AxMenu* menu)
{
    if (GetMenuController())
    {
        return GetMenuController()->ShowInitialMenu(menu);
    }

    return true;
}

// Show this menu
bool AxViewManager::ShowMenu(AxMenu* menu, AxMenuItem* parentItem, AxMenu* childMenu)
{
    if (GetMenuController())
    {
        return GetMenuController()->ShowMenu(menu, parentItem, childMenu);
    }

    return true;
}

// Show the home view
bool AxViewManager::ShowHomeView()
{
    AxSummaryView* view = wxDynamicCast(FindOrCreateView(CLASSINFO(AxSummaryView)), AxSummaryView);
    if (view)
    {
        ActivateView(view, NULL);
        view->SetDescription(GetHomeDescription());
        
        // Set the focus back to the menu control in case it changed
        // when creating a view
        GetMenuCtrl()->SetFocus();
        return true;
    }
    return true;
}

// Begin/end modal
bool AxViewManager::MakeModal(bool showModal)
{
    if (showModal)
        m_modalCount ++;
    else
        m_modalCount --;

    return true;
}

// Stops speaking.
bool AxViewManager::StopSpeech()
{
    return m_speech.GetTextToSpeech().Stop();
}

// Returns true if we can stop speaking.
bool AxViewManager::CanStopSpeech()
{
    return m_speech.GetTextToSpeech().IsSpeaking() || m_speech.GetTextToSpeech().IsPaused();
}

// Do idle processing
bool AxViewManager::IdleProcessing()
{
    if (GetMenuController())
        GetMenuController()->IdleProcessing();

    return true;
}

/// Updates the appearance of windows when settings changed.
bool AxViewManager::UpdateAppearance(wxWindow* window, int hints)
{
    if (!window)
        window = wxTheApp->GetTopWindow();

    if (GetMenuController())
        GetMenuController()->UpdateAppearance(hints);

    if (window)
    {
        wxSysColourChangedEvent event;
        event.SetEventObject(window);
        event.SetId(hints);

        (void)window->GetEventHandler()->ProcessEvent(event);
    }

    return true;
}

/*
 * Visual settings
 */

void AxVisualSettings::Init()
{
    m_textSize = -1;
}

void AxVisualSettings::Copy(const AxVisualSettings& info)
{
    m_menuItemSelectedTextColour = info.m_menuItemSelectedTextColour;
    m_menuItemDisabledTextColour = info.m_menuItemDisabledTextColour;
    m_menuItemBackgroundColour = info.m_menuItemBackgroundColour;
    m_menuItemSelectedBackgroundColour = info.m_menuItemSelectedBackgroundColour;
#if 0
    m_menuItemTextColour = info.m_menuItemTextColour;
    m_menuWindowBackgroundColour = info.m_menuWindowBackgroundColour;
    m_menuFont = info.m_menuFont;
#endif
    m_textSize = info.m_textSize;
    m_windowColour = info.m_windowColour;
    m_panelColour = info.m_panelColour;
    m_textColour = info.m_textColour;
}

// Gets the specified or default text size
int AxVisualSettings::GetBestTextSize() const
{
    if (m_textSize == -1)
        return wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).GetPointSize();
    else
        return m_textSize;
}

// Gets the specified or default text colour
wxColour AxVisualSettings::GetBestTextColour() const
{
    if (!m_textColour.IsOk())
        return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
    else
        return m_textColour;
}

// Gets the specified or default window colour
wxColour AxVisualSettings::GetBestWindowColour() const
{
    if (!m_windowColour.IsOk())
        return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
    else
        return m_windowColour;
}

// Gets the specified or default container colour
wxColour AxVisualSettings::GetBestPanelColour() const
{
    if (!m_panelColour.IsOk())
        return wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
    else
        return m_panelColour;
}

/// Load from config
bool AxVisualSettings::Load(wxConfigBase& config)
{
    config.Read(wxT("VisualSettings/TextSize"), & m_textSize);

    wxString textColour;
    if (config.Read(wxT("VisualSettings/TextColour"), & textColour) && !textColour.IsEmpty())
        m_textColour.Set(textColour);

    wxString windowColour;
    if (config.Read(wxT("VisualSettings/WindowColour"), & windowColour) && !windowColour.IsEmpty())
        m_windowColour.Set(windowColour);

    wxString panelColour;
    if (config.Read(wxT("VisualSettings/PanelColour"), & panelColour) && !panelColour.IsEmpty())
        m_panelColour.Set(panelColour);

    return true;
}

/// Save to config
bool AxVisualSettings::Save(wxConfigBase& config)
{
    config.Write(wxT("VisualSettings/TextSize"), m_textSize);

    wxString textColour;
    if (m_textColour.IsOk())
        textColour = m_textColour.GetAsString(wxC2S_HTML_SYNTAX);
    config.Write(wxT("VisualSettings/TextColour"), textColour);

    wxString windowColour;
    if (m_windowColour.IsOk())
        windowColour = m_windowColour.GetAsString(wxC2S_HTML_SYNTAX);
    config.Write(wxT("VisualSettings/WindowColour"), windowColour);

    wxString panelColour;
    if (m_panelColour.IsOk())
        panelColour = m_panelColour.GetAsString(wxC2S_HTML_SYNTAX);
    config.Write(wxT("VisualSettings/PanelColour"), panelColour);

    return true;
}
